package view.GUI.mainView;

import java.sql.SQLException;
import java.text.DecimalFormat;
import java.time.LocalDate;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.controlsfx.control.action.Action;
import org.controlsfx.dialog.DialogAction;
import org.controlsfx.dialog.Dialogs;
import org.controlsfx.validation.ValidationSupport;

import controller.Controller;
import enums.GUIText;
import enums.Message;
import enums.PaymentType;
import model.Configuration;
import model.data.interfaces.IFinishable;
import model.data.tableModel.inventory.Inventory;
import model.data.tableModel.person.Customer;
import model.data.tableModel.person.Person;
import model.data.tableModel.person.Staff;
import model.data.tableModel.sale.Bill;
import model.data.tableModel.sale.Product;
import model.data.wrapper.InputWrapper;
import model.data.wrapper.ToPay;
import view.GUI.adminView.charts.RevenueChart;
import view.GUI.adminView.coreData.people.customer.EditCustomer;
import view.GUI.adminView.coreData.people.customer.NewCustomer;
import view.GUI.adminView.coreData.people.person.EditPersonView;
import view.GUI.adminView.coreData.people.person.NewPersonView;
import view.GUI.adminView.coreData.people.staff.EditStaff;
import view.GUI.adminView.coreData.people.staff.NewStaff;
import view.GUI.adminView.coreData.product.EditProduct;
import view.GUI.adminView.coreData.product.NewProduct;
import view.GUI.adminView.inventory.InventoryView;
import view.GUI.adminView.inventory.OrderTaking;
import view.GUI.adminView.invoiceOverview.InvoiceOverview;
import view.GUI.mainView.KindVBox.BoxType;
import view.GUI.otherView.ChangePWView;
import view.GUI.otherView.Logout;
import view.GUI.otherView.PWDialog;
import view.Validator.MyValidationSupport;
import javafx.application.Application;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.Alert;
import javafx.scene.control.Alert.AlertType;
import javafx.scene.control.ButtonType;
import javafx.scene.control.ComboBox;
import javafx.scene.control.Label;
import javafx.scene.control.Menu;
import javafx.scene.control.MenuBar;
import javafx.scene.control.MenuItem;
import javafx.scene.control.ScrollPane;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import javafx.stage.Window;
import javafx.stage.WindowEvent;

/**
 * Provides the main view for this application.
 * 
 * @version %I%, %G%
 * @see KindVBox
 */
@SuppressWarnings("deprecation")
public final class MainView extends Application {

	private static Logger log = LogManager.getLogger("ERROR");

	/**
	 * The basic pane
	 */
	private VBox mainVBox;

	/**
	 * The GridPane for the top position of the BorderPane
	 */
	private GridPane topGrid;

	/**
	 * Central VBox for the center position of the basic BorderPane
	 */
	private VBox leftVBox;
	private VBox rightVBox;
	private HBox centerHBox;

	/**
	 * VBox containing the needed elements for the deposit.
	 */
	private KindVBox boxDeposit;

	/**
	 * VBox containing the needed elements for the drinks.
	 */
	private KindVBox boxDrinks;

	/**
	 * VBox containing the needed elements for the groceries.
	 */
	private KindVBox boxGroceries;

	/**
	 * Root pane in order to have a scrollbar
	 */
	private ScrollPane sp;

	/**
	 * VBox containing the needed elements for the other goods.
	 */
	private KindVBox boxOther;

	/**
	 * GridPane containing the indication of how much to pay, etc.
	 */
	private PaymentGrid paymentGrid;

	/**
	 * Label in the topGrid displaying the title
	 */
	private Label lblTopic;

	private MenuBar menuBar;
	private Menu menuEdit;
	private Menu menuNew;
	private Menu menuChart;

	private MainView instance;

	/**
	 * ComboBox in order to chose the buyer
	 */
	private ComboBox<Customer> boxCustomer;

	private Scene scene;

	private Stage primaryStage;
	private ValidationSupport valSup;
	private Controller controller;

	private Menu menuInvoice;

	private Menu menuOrder;
	private boolean alreadyChanged;

	public MainView(boolean isAdmin) {
		instance = this;
		primaryStage = new Stage();
		valSup = MyValidationSupport.createDefaultValidationSupport();
		controller = Controller.getInstance();
		controller.setMainView(this);

		createWidgets(controller.getActiveCustomer());
		addWidgets();
		setupInteraction();
		setLayout();
		if (isAdmin) {
			centerHBox.setDisable(true);
			paymentGrid.setDisable(true);
			menuBar.setDisable(false);
			addAdminItems();
		}
		try {
			start(primaryStage);
		} catch (Exception e) {
			log.error(e.getMessage());
			e.printStackTrace();
		}
	}

	public Collection<? extends IFinishable> getActiveCustomer() {
		return controller.getActiveCustomer();
	}

	/**
	 * Formats a given double value to a String with 2 digits after dezimal.
	 * 
	 * @param toFormat value to format
	 * @return formated value as String
	 */
	public static String formatNumber(double toFormat) {
		return new DecimalFormat("#0.00").format(toFormat) + " EUR";
	}

	/**
	 * Closes the stage.
	 * <p>
	 * Is invoked if a logout-attempt was successful.
	 */
	public void logoutSuccess() {
		primaryStage.close();
	}

	/**
	 * Returns the values that must be paid.
	 * <p>
	 * Fetches the input and forwards them to the model for computation. Returns
	 * the values wrapped in an object of <code>ToPay</code>.
	 * 
	 * @return wrapped values
	 */
	public ToPay getComputedInput() {
		Map<BoxType, List<InputWrapper>> inputMap = new HashMap<BoxType, List<InputWrapper>>();

		inputMap.put(boxDeposit.getType(), boxDeposit.getInput());
		inputMap.put(boxDrinks.getType(), boxDrinks.getInput());
		inputMap.put(boxGroceries.getType(), boxGroceries.getInput());
		inputMap.put(boxOther.getType(), boxOther.getInput());

		ToPay toPay = controller.computeInputValue(inputMap);
		toPay.setCustomer(boxCustomer.getSelectionModel().selectedItemProperty().getValue());
		return toPay;
	}

	/**
	 * Resets the input.
	 * <p>
	 * Is invoked when a <code>Bill</code> was successfully written to the
	 * database.
	 */
	public void resetInput() {

		boxDeposit.resetInput();
		boxDrinks.resetInput();
		boxGroceries.resetInput();
		boxOther.resetInput();
		boxCustomer.getSelectionModel().clearSelection();
		boxCustomer.getSelectionModel().select(controller.getCustomer("privater Kauf"));
		paymentGrid.resetInput();

	}

	// HELPER METHODS below

	private void setupInteraction() {
		primaryStage.setOnCloseRequest(new EventHandler<WindowEvent>() {

			@Override
			public void handle(WindowEvent event) {
				event.consume();
				new Logout(primaryStage);
			}
		});
		boxCustomer.getSelectionModel().selectedItemProperty().addListener(new ChangeListener<Customer>() {

			@Override
			public void changed(ObservableValue<? extends Customer> observable, Customer oldValue, Customer newValue) {
				paymentGrid.showComputedValue();
				if (newValue != null) {
					if (!checkIfBillIsPaid(newValue)) {
						Alert alert = new Alert(AlertType.WARNING);
						alert.setTitle("Achtung");
						alert.setContentText(Message.BILLISNTPAID.toString());

						alert.showAndWait();
						alreadyChanged = true;
						boxCustomer.getSelectionModel().select(oldValue);
						alreadyChanged = false;
					} else{
						if(!alreadyChanged && (!boxDeposit.getInput().isEmpty() || !boxDrinks.getInput().isEmpty() || !boxGroceries.getInput().isEmpty()|| !boxOther.getInput().isEmpty())){
							Alert alert = new Alert(AlertType.CONFIRMATION);
							alert.setTitle("Bist du sicher?");
							alert.setContentText(Message.CHANGECUSTOMER.toString());
							
							Optional<ButtonType> result = alert.showAndWait();
							if (result.get() == ButtonType.OK){
								alreadyChanged = false;
							} else {
								alreadyChanged = true;
								boxCustomer.getSelectionModel().select(oldValue);									
							}
						} else{
							alreadyChanged = false;
						}
					}	
				}
			}
		});
	}

	private boolean checkIfBillIsPaid(Customer customer) {

		return controller.checkIfBillIsPaid(customer);
	}

	private void addWidgets() {

		menuBar.getMenus().add(menuEdit);
		// Adding nodes to the grid in the top of the BorderPane

		topGrid.add(lblTopic, 0, 1);
		topGrid.add(new Label(), 0, 2);

		// Adding the VBoxes of the different goods to the central VBox in the

		leftVBox.getChildren().add(topGrid);
		leftVBox.getChildren().add(boxCustomer);
		leftVBox.getChildren().add(boxDeposit);
		leftVBox.getChildren().add(boxDrinks);
		leftVBox.getChildren().add(boxGroceries);
		leftVBox.getChildren().add(boxOther);

		rightVBox.getChildren().add(paymentGrid);

		centerHBox.getChildren().addAll(leftVBox, rightVBox);

		// Adding all sub layout manager to the basic pane
		mainVBox.getChildren().addAll(menuBar, centerHBox);

		sp.setContent(mainVBox);

	}

	private void setLayout() {
		topGrid.getStyleClass().add("grid");
		lblTopic.setMinSize(500, 40);
		boxCustomer.setPrefSize(175, 15);
		lblTopic.setId("topic");
		sp.setFitToHeight(true);
		sp.setFitToWidth(true);
		sp.setVisible(true);
		centerHBox.setId("backGroundImage");
		leftVBox.getStyleClass().add("vbox");
		menuBar.setMinHeight(45);
		boxCustomer.setId("box-customer");
		// centerHBox.getStyleClass().add("hbox");

	}

	private void createWidgets(Collection<Customer> collection) {
		mainVBox = new VBox();
		topGrid = new GridPane();
		leftVBox = new VBox();
		rightVBox = new VBox();
		boxDeposit = new KindVBox(BoxType.DEPOSIT, this, controller.getProducts(false, BoxType.DEPOSIT));
		boxDrinks = new KindVBox(BoxType.DRINKS, this, controller.getProducts(false, BoxType.DRINKS));
		boxGroceries = new KindVBox(BoxType.GROCERIES, this, controller.getProducts(false, BoxType.GROCERIES));
		boxOther = new KindVBox(BoxType.OTHER, this, controller.getProducts(false, BoxType.OTHER));
		// boxBottom = new HBox();
		paymentGrid = PaymentGrid.getInstance(this);

		lblTopic = new Label(GUIText.BLUEHOUSESHOP.toString());
		boxCustomer = new ComboBox<>();
		List<Customer> l;
		if (collection instanceof List) {
			l = (List<Customer>) collection;
			Collections.sort(l);
		}
		boxCustomer.getItems().addAll(collection);
		for (Customer c : collection) {
			if (c.getPayment() == PaymentType.CASH) {
				boxCustomer.getSelectionModel().select(c);
			}
		}
		sp = new ScrollPane();
		centerHBox = new HBox();

		menuBar = new MenuBar();
		addItems();
	}

	private void addItems() {

		menuEdit = new Menu(GUIText.EDIT.toString());

		MenuItem itemSetting = new MenuItem(GUIText.CHANGEPW.toString());
		menuEdit.getItems().add(itemSetting);

		itemSetting.setOnAction(new EventHandler<ActionEvent>() {

			@Override
			public void handle(ActionEvent arg0) {
				new ChangePWView(instance);
			}
		});
	}

	public void createInvoice(String password) {
		try {
			controller.createInvoice(password);
		} catch (IllegalArgumentException | SQLException e) {
			Dialogs.create()
					.message(GUIText.ALREADYINVOICED.toString())
					.showInformation();
		}

	}

	private void addAdminItems() {
		menuChart = new Menu(GUIText.CHART.toString());
		MenuItem revenueChart = new MenuItem(GUIText.REVENUE.toString());
		revenueChart.setOnAction(new EventHandler<ActionEvent>() {

			@Override
			public void handle(ActionEvent event) {
				new RevenueChart(instance);
			}
		});
		menuChart.getItems().add(revenueChart);

		MenuItem editProduct = new MenuItem(GUIText.PRODUCT.toString());
		editProduct.setOnAction(new EventHandler<ActionEvent>() {

			@Override
			public void handle(ActionEvent arg0) {
				new EditProduct(instance, GUIText.EDIT);
			}
		});
		MenuItem editPerson = new MenuItem(GUIText.PERSON.toString());
		editPerson.setOnAction(new EventHandler<ActionEvent>() {

			@Override
			public void handle(ActionEvent event) {
				new EditPersonView(instance);
			}
		});
		MenuItem editStaff = new MenuItem(GUIText.STAFF.toString());
		editStaff.setOnAction(new EventHandler<ActionEvent>() {

			@Override
			public void handle(ActionEvent arg0) {
				new EditStaff(instance);
			}
		});

		MenuItem editCustomer = new MenuItem(GUIText.CUSTOMER.toString());
		editCustomer.setOnAction(new EventHandler<ActionEvent>() {

			@Override
			public void handle(ActionEvent event) {
				new EditCustomer(instance);
			}
		});
		menuEdit.getItems().addAll(editStaff, editCustomer, editPerson, editProduct);

		menuInvoice = new Menu(GUIText.ABRECHNUNG.toString());
		MenuItem itemInvoice = new MenuItem(GUIText.ABRECHNUNGENERSTELLEN.toString());
		MenuItem itemInventory = new MenuItem(GUIText.INVENTORY.toString());
		menuInvoice.getItems().addAll(itemInvoice, itemInventory);

		itemInventory.setOnAction(new EventHandler<ActionEvent>() {

			@Override
			public void handle(ActionEvent event) {
				new InventoryView(instance);
			}
		});

		itemInvoice.setOnAction(new EventHandler<ActionEvent>() {

			@Override
			public void handle(ActionEvent arg0) {
				new PWDialog(instance, GUIText.ENTERPW.toString());
			}
		});

		MenuItem itemInvoiceOverview = new MenuItem(GUIText.INVOICE.toString() + " " + GUIText.OVERVIEW);
		itemInvoiceOverview.setOnAction(new EventHandler<ActionEvent>() {

			@Override
			public void handle(ActionEvent event) {
				new InvoiceOverview(instance);
			}
		});
		menuInvoice.getItems().add(itemInvoiceOverview);

		menuOrder = new Menu(GUIText.ORDER.toString());
		MenuItem itemTakeOrder = new MenuItem(GUIText.TAKEANORDER.toString());
		menuOrder.getItems().add(itemTakeOrder);
		itemTakeOrder.setOnAction(new EventHandler<ActionEvent>() {

			@Override
			public void handle(ActionEvent arg0) {
				new OrderTaking(instance);
			}
		});

		menuNew = new Menu(GUIText.NEW.toString());
		MenuItem itemNewPerson = new MenuItem(GUIText.PERSON.toString());
		MenuItem itemNewCustomer = new MenuItem(GUIText.CUSTOMER.toString());
		MenuItem itemNewStaff = new MenuItem(GUIText.STAFF.toString());
		MenuItem itemNewProduct = new MenuItem(GUIText.PRODUCT.toString());
		itemNewProduct.setOnAction(new EventHandler<ActionEvent>() {

			@Override
			public void handle(ActionEvent arg0) {
				new NewProduct(instance);
			}
		});
		itemNewCustomer.setOnAction(new EventHandler<ActionEvent>() {

			@Override
			public void handle(ActionEvent arg0) {
				new NewCustomer(instance);
			}
		});
		itemNewStaff.setOnAction(new EventHandler<ActionEvent>() {

			@Override
			public void handle(ActionEvent event) {
				new NewStaff(instance);
			}
		});
		itemNewPerson.setOnAction(new EventHandler<ActionEvent>() {

			@Override
			public void handle(ActionEvent event) {
				new NewPersonView(instance);
			}
		});

		menuNew.getItems().addAll(itemNewStaff, itemNewCustomer, itemNewPerson, itemNewProduct);

		menuBar.getMenus().add(menuNew);
		menuBar.getMenus().add(menuInvoice);
		menuBar.getMenus().add(menuOrder);
		menuBar.getMenus().add(menuChart);

	}

	// GETTER AND SETTER BELOW

	public ValidationSupport getValSup() {
		return valSup;
	}

	public Staff getWorkingStaff() {
		return controller.getWorkingStaff();
	}

	public Window getPrimaryStage() {
		return primaryStage;
	}

	public PaymentGrid getPaymentGrid() {
		return paymentGrid;
	}

	public Product getProduct(String newValue) {
		return controller.getProduct(newValue);
	}

	public List<Product> getProducts(boolean mostUpdated, BoxType type) {
		return controller.getProducts(mostUpdated, type);
	}

	public boolean checkIfOldPWEquals(String value) {
		return controller.checkIfOldPWEquals(value);
	}

	public void setNewPassword(Staff staff, String newPw) {
		if (controller.setNewPassword(staff, newPw)) {
			Dialogs.create().owner(primaryStage).message(Message.SETPWOKAY.toString()).showInformation();

		} else {
			Dialogs.create().owner(primaryStage).message(Message.SETPWERROR.toString()).showError();
		}
	}

	public void takeOrder(Map<BoxType, List<InputWrapper>> order) {
		controller.takeOrder(order);
	}

	public List<Bill> getActiveDebts() {
		return controller.getActiveDebts();
	}

	public List<Bill> getBills() {

		return controller.getBills();
	}

	public List<Person> getPersons() {
		return controller.getPersons();
	}

	public void createInventoryInvoice(List<List<InputWrapper>> inputList, Inventory inventory) {
		controller.createInventoryInvoice(inputList, inventory);
	}

	public Collection<Staff> getStaff() {
		return controller.getStaff();
	}

	public void deleteStaff(Staff s) {
		controller.deleteStaff(s);
	}

	public void deleteCustomer(Customer c) {
		controller.deleteCustomer(c);
	}

	public void addStaff(Staff s) {
		controller.addStaff(s);
	}

	public void addProduct(Product newProduct) {
		controller.addProduct(newProduct);
	}

	public void deleteProduct(Product replacement) {
		controller.deleteProduct(replacement);
	}

	@Override
	public void start(Stage primaryStage) throws Exception {
		scene = new Scene(sp);
		scene.getStylesheets().add(getClass().getResource(Configuration.STYLEVALIDATION).toExternalForm());
		scene.getStylesheets().add(getClass().getResource(Configuration.STYLEMAINVIEW).toExternalForm());
		scene.getStylesheets().add(getClass().getResource(Configuration.STYLEROOT).toExternalForm());
		this.primaryStage = primaryStage;
		primaryStage.setScene(scene);
		primaryStage.setTitle(GUIText.BLUEHOUSESHOP.toString());
		primaryStage.setResizable(true);
		primaryStage.setMinHeight(700);
		primaryStage.setMinWidth(700);
		primaryStage.setFullScreen(true);
		primaryStage.show();
	}

	public void bookPurchase(ToPay toPay) {
		controller.bookPurchase(toPay);
	}

	public Map<String, Number> getChartData(boolean isRevenue, List<IFinishable> selectedCustomer, LocalDate begin, LocalDate end) {
		return controller.getRevenueData(isRevenue, selectedCustomer, begin, end);
	}

	public List<Product> getProductGroup(int groupID) {
		return controller.getProductGroup(groupID);
	}
}
